package proiectpcbe;

import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author bufu
 */
public class Comert {

    /* Lock pentru modificarea ofertelor*/
    private final Object m_lockOferte = new Object();
    /* Ofertele existent*/
    private ArrayList<Oferta> m_oferte;
    /* Lock for statistics */
    private final Object m_lockStatistics = new Object();
    /* Statistics not used */
    private HashMap<Resursa, Integer> m_notUsed;
    /* Statistics acomplished */
    private HashMap<Resursa, Integer> m_Used;

    /**
     *
     */
    public Comert() {
        m_oferte = new ArrayList<Oferta>();
        m_notUsed = new HashMap<Resursa, Integer>();
        m_notUsed.put(Resursa.LEMN, 0);
        m_notUsed.put(Resursa.METAL, 0);
        m_notUsed.put(Resursa.PIATRA, 0);
        m_Used = new HashMap<Resursa, Integer>();
        m_Used.put(Resursa.LEMN, 0);
        m_Used.put(Resursa.METAL, 0);
        m_Used.put(Resursa.PIATRA, 0);
    }

    public void afiseazaStatisticile() {
        synchronized (m_lockStatistics) {
            System.out.println("Comert:Resurse nefolosite: " + m_notUsed.get(Resursa.LEMN) + " lemn " + m_notUsed.get(Resursa.PIATRA) + " piatra " + m_notUsed.get(Resursa.METAL) + " metal.");
            System.out.println("Comert:Resurse folosite: " + m_Used.get(Resursa.LEMN) + " lemn " + m_Used.get(Resursa.PIATRA) + " piatra " + m_Used.get(Resursa.METAL) + " metal.");
        }
    }

    private class Oferta {

        private int cantitate;
        private Resursa oferita;
        private Resursa ceruta;
        private Jucator player;

        /**
         *
         * @param cantitate
         * @param oferita
         * @param ceruta
         * @param player
         */
        private Oferta(int cantitate, Resursa oferita, Resursa ceruta, Jucator player) {
            this.cantitate = cantitate;
            this.oferita = oferita;
            this.ceruta = ceruta;
            this.player = player;
        }
    }

    private class Realizat {

        private int cantitate;
        private Resursa res;
        private Jucator j;

        /**
         *
         * @param j
         * @param r
         * @param c
         */
        private Realizat(Jucator j, Resursa r, int c) {
            cantitate = c;
            res = r;
            this.j = j;
        }
    }

    private class AdaugaOferta extends Thread {

        private Oferta oferta;
        private boolean debug = false;

        /**
         *
         * @param o
         */
        private AdaugaOferta(Oferta o) {
            oferta = o;
        }

        @Override
        public void run() {
            ArrayList<Realizat> realizate = new ArrayList<Realizat>();
            Oferta current;
            float rata = oferta.oferita.conversie(oferta.ceruta);
            float inversRata = oferta.ceruta.conversie(oferta.oferita);
            int cantitateaCeruta = (int) (rata * oferta.cantitate);
            if (debug) {
                System.out.println(getName() + "Add run oferita:" + oferta.oferita + ",cant:" + cantitateaCeruta + ",ceruta:" + oferta.ceruta + ",rata" + rata + ",irata:" + inversRata);
            }
            if ((int) (cantitateaCeruta * inversRata) != oferta.cantitate) {
                realizate.add(new Realizat(oferta.player, oferta.oferita, oferta.cantitate - (int) (cantitateaCeruta * inversRata)));
            }
            //System.out.println(Thread.currentThread().getName() + "adauga oferta, cant:" + cantitateaCeruta);
            synchronized (m_lockOferte) {
                for (int i = 0; i < m_oferte.size() && cantitateaCeruta > 0; i++) {
                    current = m_oferte.remove(i);
                    if (current.ceruta == oferta.oferita && current.oferita == oferta.ceruta) {
                        if (cantitateaCeruta < current.cantitate) {
                            realizate.add(new Realizat(current.player, oferta.oferita, (int) (cantitateaCeruta * inversRata)));
                            realizate.add(new Realizat(oferta.player, oferta.ceruta, cantitateaCeruta));
                            current.cantitate -= cantitateaCeruta;
                            m_oferte.add(i, current);
                            cantitateaCeruta = 0;
                        } else {
                            realizate.add(new Realizat(current.player, oferta.oferita, (int) (current.cantitate / rata)));
                            realizate.add(new Realizat(oferta.player, oferta.ceruta, current.cantitate));
                            cantitateaCeruta -= current.cantitate;
                        }
                    } else {
                        m_oferte.add(i, current);
                    }
                }
                if (cantitateaCeruta > 0) {
                    m_oferte.add(new Oferta((int) (cantitateaCeruta * inversRata), oferta.oferita, oferta.ceruta, oferta.player));
                    synchronized (m_lockStatistics) {
                        m_notUsed.put(oferta.oferita, m_notUsed.get(oferta.oferita) + cantitateaCeruta);
                    }
                }
            }
            for (Realizat res : realizate) {
                res.j.vinResurseDinComert(res.cantitate, res.res);
                synchronized (m_lockStatistics) {
                    m_Used.put(res.res, m_Used.get(res.res) + res.cantitate);
                }
            }
        }
    }

    private class AnuleazaOferta extends Thread {

        private Jucator jucator;
        private Resursa oferita, primaCeruta, aDouaCeruta;
        private int cant;
        private ArrayList<Realizat> realizate;
        private boolean debug = false;

        /**
         *
         * @param cantitate
         * @param oferita
         * @param primaCeruta
         * @param aDouaCeruta
         * @param j
         */
        private AnuleazaOferta(int cantitate, Resursa oferita, Resursa primaCeruta, Resursa aDouaCeruta, Jucator j) {
            this.jucator = j;
            this.oferita = oferita;
            this.cant = cantitate;
            this.primaCeruta = primaCeruta;
            this.aDouaCeruta = aDouaCeruta;
            realizate = new ArrayList<Realizat>();
        }

        /**
         *
         * @param o
         * @return
         */
        private Oferta incearca(Oferta o) {
            if (o.player != jucator || o.oferita != oferita) {
                return o;
            }
            if (debug) {
                System.out.println(this.getName() + "Rem incearca,cant " + o.cantitate);
            }
            if (cant > o.cantitate) {
                realizate.add(new Realizat(jucator, oferita, o.cantitate));
                cant -= o.cantitate;
            } else {
                realizate.add(new Realizat(jucator, oferita, cant));
                (new AdaugaOferta(new Oferta(o.cantitate - cant, oferita, o.ceruta, jucator))).start();
                cant = 0;
            }
            return null;
        }

        /**
         *
         * @param res
         */
        private void anuleazaCuResursa(Resursa res) {
            if (debug) {
                System.out.println(this.getName() + "Rem anuleazaCuResursa " + res + ",cant " + cant + ",oferte:" + m_oferte.size());
            }
            Oferta current, temp;
            for (int i = 0; i < m_oferte.size() && cant > 0; i++) {
                temp = m_oferte.remove(i);
                if (temp.ceruta != res) {
                    m_oferte.add(i, temp);
                    continue;
                }
                current = incearca(temp);
                if (current != null) {
                    m_oferte.add(i, current);
                }
            }
        }

        @Override
        public void run() {
            if (debug) {
                System.out.println(this.getName() + "Rem run cu " + cant + " " + oferita + ",prima " + primaCeruta + ", a doua " + aDouaCeruta);
            }
            synchronized (m_lockOferte) {
                anuleazaCuResursa(primaCeruta);
                anuleazaCuResursa(aDouaCeruta);
            }
            if (debug) {
                System.out.println(this.getName() + " realizate " + realizate.size());
            }
            for (Realizat res : realizate) {
                res.j.vinResurseDinComert(res.cantitate, res.res);
                synchronized (m_lockStatistics) {
                    m_Used.put(res.res, m_Used.get(res.res) + res.cantitate);
                }
            }
        }
    }

    /**
     * Pune o oferta.
     *
     * @param j jucatorul care pune oferta
     * @param cantitate oferita
     * @param oferita resursa care e oferita
     * @param ceruta resursa care e ceruta
     */
    public void puneOferta(Jucator j, int cantitate, Resursa oferita, Resursa ceruta) {
        if (cantitate > 0) {
            (new AdaugaOferta(new Oferta(cantitate, oferita, ceruta, j))).start();
        }
    }

    /**
     * Incearca sa anuleze o oferta.
     *
     * @param j care incearca sa anuleze
     * @param oferita care a fost oferita
     * @param cantitate cantitatea care se doreste sa fie anulata
     * @param primaCeruta ofertele cu aceasta resursa ceruta vor fi anulate
     * primele
     * @param aDouaCeruta daca nu se atinge cantitatea anulata se vor anula
     * ofertele cu aceasta resursa
     */
    public void anuleazaOferta(Jucator j, Resursa oferita, int cantitate, Resursa primaCeruta, Resursa aDouaCeruta) {
        (new AnuleazaOferta(cantitate, oferita, primaCeruta, aDouaCeruta, j)).start();
    }
}
